// import { createApp } from 'vue';
import ComponentFetcher from './remoteComponentFetcher.js';
import { ComponentDefinitionHelper } from '../ComponentTemplate/componentDefinition';
import patchComponent from '../ComponentTemplate/patchComponent';
import { interpretComponentDefinitionFromPackage } from './componentInterpreter';
import Notifier from '../Utils/Notifier';
import _ from 'lodash';
import { CPNT_REF_PREFIX_ENUM } from '@CIF/Runtime/Global/variable';
// const Vue = createApp();
// console.log('Vue', Vue);
let ComponentStateMap = {};

/**
 * COMPONENT_STATE_ENUM
 */
export const CPNT_STATE_ENUM = {
	UNLOAD: 'UNLOAD',
	UNLOAD_JS: 'UNLOAD_JS',
	LOADING_JS: 'LOADING_JS',
	LOADED_JS: 'LOADED_JS',
	EXIST_CSS: 'EXIST_CSS',
	UNLOAD_CSS: 'UNLOAD_CSS',
	LOADING_CSS: 'LOADING_CSS',
	LOADED_CSS: 'LOADED_CSS'
};

/**
 * COMPONENT_STATE_QUERY_ENUM
 */
const CS_QUERY_ENUM = {
	UNLOAD: 0b00000001,
	UNLOAD_JS: 0b00000010,
	LOADING_JS: 0b00000100,
	LOADED_JS: 0b00001000,
	EXIST_CSS: 0b00010000,
	UNLOAD_CSS: 0b00100000,
	LOADING_CSS: 0b01000000,
	LOADED_CSS: 0b10000000
};

/**
 * COMPONENT_STATE_SETUP_ENUM
 */
const CS_SETUP_ENUM = {
	UNLOAD: 0b11110001,
	UNLOAD_JS: 0b11110010,
	LOADING_JS: 0b11110100,
	LOADED_JS: 0b11111000,
	EXIST_CSS: 0b00011111,
	UNLOAD_CSS: 0b00101111,
	LOADING_CSS: 0b01001111,
	LOADED_CSS: 0b10001111
};

/**
 * ComponentStateMap里的值以二进制表示，从右向左，是为1，否为0
 * 第1位表示组件是否从未加载
 * 第2位表示组件是否从未加载js
 * 第3位表示组件是否正在加载js
 * 第4位表示组件是否完成加载js
 * 第5位表示组件是否存在css
 * 第6位表示组件是否未加载css
 * 第7位表示组件是否正在加载css
 * 第8位表示组件是否完成加载css
 */
export const ComponentStateHelper = {
	getState(id, flag) {
		if (!CPNT_STATE_ENUM[flag]) {
			throw new Error('unknow component state enum:', flag);
		}
		return ((ComponentStateMap[id] || 0b00000001) & CS_QUERY_ENUM[flag]) === CS_QUERY_ENUM[flag];
	},
	setState(id, flag) {
		if (!CPNT_STATE_ENUM[flag]) {
			throw new Error('unknow component state enum:', flag);
		}
		ComponentStateMap[id] =
			((ComponentStateMap[id] || 0b00000001) & CS_SETUP_ENUM[flag]) | CS_QUERY_ENUM[flag];
	},
	reset() {
		ComponentStateMap = {};
	}
};

// 异常: 组件未定义
const COMPONENT_UNDEFINED = (versionedName) =>
	`${versionedName}组件挂载失败，请检查组件是否被正确编译打包！`;
/**
 * @function initError
 * @description 构造异常
 * @param { string } key 异常类型
 * @returns { Error } 异常
 */
export const initError = (fn, value) => {
	throw new Error(fn(value));
	// return new Error(fn(value));
};

export const getUniqueVersionName = (cpnt) => {
	return `${cpnt.moduleName}_${cpnt.version}`;
};

const isTheSameComponents = (oldCpnt = {}, newCpnt = {}) => {
	if (!oldCpnt.name || !oldCpnt.version || !newCpnt.moduleName) {
		return false;
	}
	return getUniqueVersionName(oldCpnt) === getUniqueVersionName(newCpnt);
};

/**
 * @class DynamicComponentLoader
 * @description 异步组件加载器
 * @member { array } components 组件列表
 * @member { HTMLLinkElement } css 当前挂载的 CSS 节点
 * @member { HTMLScriptElement } javascript 当前挂载的 JavaScript 节点
 * @method load 加载组件
 * @author yin_s
 */
class DynamicComponentLoader {
	// remotes = []
	components = [];
	styleCache = {};
	scriptCache = {};

	/**
	 * @constructor
	 * @param { array } remotes 数组中 remote 对象包含四个属性: moduleName, version, file, fileToken
	 * @param { Funtion } callback 组件远程加载结束的回调
	 */

	fetch(cpntLibEntry = {}, reset = true) {
		reset && Notifier.resetFetchingProgress();
		return new Promise((resolve = () => {}, reject = () => {}) => {
			const fetchedCpnt = this.components.find((item) => isTheSameComponents(item, cpntLibEntry));
			if (fetchedCpnt) {
				resolve(this);
			} else {
				ComponentFetcher.fetchComponent(cpntLibEntry)
					.then((result) => {
						this.components = this.components.concat(result);
						resolve(this);
					})
					.catch((e) => {
						reject(e);
					});
			}
		});
	}

	/**
   * 未作相同组件过滤处理，即使组件已经存在，也会重复下载
   * 在补充过滤逻辑之前应尽量仅在应用初始化时调用
   * @param {*} cpntLibEntryList 
   * * [{
        definitionId: cpntLibrary.id,
        name: cpntLibrary.name,
        package: cpntLibrary.package,
      }]
   * @returns 
   */
	fetchAll(cpntLibEntryList = [], reset = true) {
		reset && Notifier.resetFetchingProgress();
		return new Promise((resolve = () => {}, reject = () => {}) => {
			ComponentFetcher.fetchComponentAll(cpntLibEntryList).then(
				([results = [], failedReason = null]) => {
					this.components = this.components.concat(results);
					resolve([this, failedReason]);
				}
			);
		});
	}
	hasTwoComponents(tree, componentName = '组件1') {
		let count = 0;
		// 递归函数来遍历树
		function traverse(nodes) {
			for (let node of nodes) {
				if (node.name === componentName) {
					count++;
					if (count >= 2) {
						return true;
					}
				}
				if (node.children && node.children.length > 0) {
					if (traverse(node.children)) {
						return true;
					}
				}
			}
			return false;
		}

		return traverse(tree);
	}
	/**
	 * @name dispose
	 * @description 清理已挂载的组件
	 * @param { string } versionedName 组件名称, 不传参则清除所有已挂载的组件
	 */
	dispose(versionedName = '', id, cpntTree, cpntName) {
		// debugger;
		// 清理单个组件的方法
		const disposeOne = (name) => {
			if (!this.hasTwoComponents(cpntTree, cpntName)) {
				if (this.styleCache[name]) {
					document.head.removeChild(this.styleCache[name]);
					delete this.styleCache[name];
				}
				if (this.scriptCache[name]) {
					document.body.removeChild(this.scriptCache[name]);
					delete this.scriptCache[name];
				}
				id && ComponentStateHelper.setState(id, CPNT_STATE_ENUM.UNLOAD_CSS);
			}
		};

		if (versionedName) {
			disposeOne(versionedName);
		} else {
			Object.keys(this.scriptCache).forEach(disposeOne);
			ComponentStateHelper.reset();
		}
	}

	loadCss(component, versionedName) {
		if (component.cssBlob) {
			ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.LOADING_CSS);
			const reader = new FileReader();
			// This fires after the blob has been read/loaded.
			reader.addEventListener('loadend', (e) => {
				const text = e.target.result;
				const style = document.createElement('style');
				style.type = 'text/css';
				style.innerHTML = text; //component.cssBlob;
				style.id = `${versionedName}.css`;
				document.head.appendChild(style);
				this.styleCache[versionedName] = style;
				ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.LOADED_CSS);
			});
			// Start reading the blob as text.
			reader.readAsText(component.cssBlob);
		}
	}

	/**
	 * 将组件异步挂载到html中
	 * @name load
	 * @description 异步组件加载
	 * @param { string } versionedName 组件名称
	 * @returns { Promise } 返回一个 Promise 对象
	 */
	load(versionedName) {
		const component = this.components.find((item) => getUniqueVersionName(item) === versionedName);

		return new Promise((resolve1 = () => {}, reject1 = () => {}) => {
			if (!component) {
				reject1(initError(COMPONENT_UNDEFINED, versionedName));
			}

			if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.UNLOAD)) {
				ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.UNLOAD_JS);
				ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.UNLOAD_CSS);
			}

			if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.UNLOAD_CSS)) {
				this.loadCss(component, versionedName);
			}

			if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.UNLOAD_JS)) {
				// javascript
				component.script.loadlistener = 0;
				component.script.onload = () => {
					// window 对象中的 name 来自 组件打包时 vue-cli-service 的 --name 选项指定的值
					if (window[versionedName]) {
						const target = window[versionedName];
						patchComponent(target);
						window.vueInstance.component(CPNT_REF_PREFIX_ENUM.CPNT_DEF + target.name, target);
						// console.log('111window.vueInstance', window.vueInstance);
						ComponentDefinitionHelper.setDef(
							interpretComponentDefinitionFromPackage(component.id, target)
						);
						this.scriptCache[versionedName] = component.script;
						resolve1(target.name);
						ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.LOADED_JS);

						for (let i = 1; i <= component.script.loadlistener; i++) {
							component.script.dispatchEvent(
								new CustomEvent(`loadCpnt_${versionedName}_${i}`, {
									detail: target.name
								})
							);
						}
					} else {
						for (let i = 1; i <= component.script.loadlistener; i++) {
							component.script.dispatchEvent(
								new CustomEvent(`loadCpnt_${versionedName}_${i}`, {
									detail: null
								})
							);
						}
						component.status = 'failed';
						reject1(initError(COMPONENT_UNDEFINED, versionedName));
					}
				};
				// debugger;
				ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.LOADING_JS);
				document.body.appendChild(component.script);
			} else {
				if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.LOADING_JS)) {
					if (component.script.onload) {
						if (component.status === 'failed') {
							reject1(initError(COMPONENT_UNDEFINED, versionedName));
						} else {
							component.script.loadlistener = component.script.loadlistener + 1;
							component.script.addEventListener(
								`loadCpnt_${versionedName}_${component.script.loadlistener}`,
								(e) => {
									e.detail
										? resolve1(e.detail)
										: reject1(initError(COMPONENT_UNDEFINED, versionedName));
								}
							);
						}
					} else {
						reject1(initError(COMPONENT_UNDEFINED, versionedName));
						return;
					}
				} else if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.LOADED_JS)) {
					if (window[versionedName]) {
						const target = window[versionedName];
						// ComponentDefinitionHelper.setDef(interpretComponentDefinitionFromPackage(component.id, target))
						resolve1(target.name);
						return;
					} else {
						reject1(initError(COMPONENT_UNDEFINED, versionedName));
						return;
					}
				} else {
					reject1(initError(COMPONENT_UNDEFINED, versionedName));
					return;
				}
			}
		});
	}

	/**
	 * 加载全部组件, 初始化时用
	 */
	loadAll(instance) {
		return new Promise((resolve1 = () => {}, reject1 = () => {}) => {
			// if (_.get(window, "cif.config.logger.enable", false) === true) {
			//   console.log("CIF.C.dynamicComponentLoader:loadAll", this.components)
			// }
			if (_.isEmpty(this.components)) {
				resolve1([this.components, null]);
			} else {
				Promise.allSettled(
					this.components.map((component) => {
						return new Promise((resolve2 = () => {}, reject2 = () => {}) => {
							const versionedName = getUniqueVersionName(component);
							if (!component) {
								reject2(initError(COMPONENT_UNDEFINED, versionedName));
							}

							if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.UNLOAD)) {
								ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.UNLOAD_JS);
								ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.UNLOAD_CSS);
							}

							if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.UNLOAD_CSS)) {
								this.loadCss(component, versionedName);
							}

							if (ComponentStateHelper.getState(component.id, CPNT_STATE_ENUM.UNLOAD_JS)) {
								// javascript
								component.script.loadlistener = 0;
								component.script.onload = () => {
									// window 对象中的 name 来自 组件打包时 vue-cli-service 的 --name 选项指定的值
									if (window[versionedName]) {
										const target = window[versionedName];
										patchComponent(target);
										instance.use(target);
										ComponentDefinitionHelper.setDef(
											interpretComponentDefinitionFromPackage(component.id, target)
										);
										this.scriptCache[versionedName] = component.script;
										resolve2(target.name);
										ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.LOADED_JS);
									} else {
										component.status = 'failed';
										reject2(initError(COMPONENT_UNDEFINED, versionedName));
									}
								};
								ComponentStateHelper.setState(component.id, CPNT_STATE_ENUM.LOADING_JS);
								document.body.appendChild(component.script);
							}
						});
					})
				).then((results) => {
					resolve1([
						results.filter((x) => x.status === 'fulfilled').map((x) => x.value),
						results.filter((x) => x.status === 'rejected').map((x) => x.reason)
					]);
				});
			}
		});
	}

	/**
	 * 加载本地组件
	 * 将组件可配置项解析成组件定义，并放到组件定义 ComponentDefinition 字典里
	 * @param {*} target
	 * @param {*} id 本地开发临时组件定义id
	 */
	loadLocal(vm, target, id) {
		vm.use(target);
		target.__isLocalDev = true;

		ComponentDefinitionHelper.setDef(interpretComponentDefinitionFromPackage(id, target));
		ComponentStateHelper.setState(id, CPNT_STATE_ENUM.LOADED_JS);
	}
}

export default DynamicComponentLoader;
